Particles2D

Particles2D draws textures to create visual effects

Particles2D spawns and animates sprites to create all sorts of visual effects. It’s Godot’s equivalent of 2D particle emitters in other game engines.

Their primary use is to add to the game feel and visual flair of existing actions. You can also use particles to draw many instances of a texture efficiently using the GPU.

In this guide, you will learn to:

Contents:

Setting up a Particles2D node

To create a particle system, you need to:

  1. Create a Particles2D node.
  2. Assign a material to its Process Material -> Material property.

The node itself emits particles. It also controls their textures, the number of particles to spawn, and their lifetime. The material controls each particle’s motion and color.

You can either use a ShaderMaterial or a ParticlesMaterial for the Process Material -> Material.

The ShaderMaterial lets you code a shader and control particles manually. The built-in ParticlesMaterial comes with dozens of valuable properties. They allow us to make the particles accelerate, slow down, orbit, and more.

Particles2D’s key features

To control the particles’ emission, we use the Emitting property. Enabling it emits new particles.

Note: This property only has effect when Particles2D has a ParticlesMaterial or ShaderMaterial in its Process Material -> Material property.

Emitting false Emitting true

The Amount determines how many particles to emit during one cycle of the duration set in Lifetime.

For example, with Lifetime set to 1.0, and Amount set to 8, the Particles2D emits eight particles in one second. Each particle stays alive for one second.

Here are some examples of different Amount settings with a Lifetime of one second:

4 Amount 8 Amount 16 Amount

In contrast, here are varying Lifetime examples, with an Amount of 8:

0.1 Lifetime 0.5 Lifetime 1.0 Lifetime

To control the interval between emitting particles, we can adjust the Explosiveness. A high Explosiveness makes particles spawn in a shorter time span.

When we want particles to emit at once, like for a water splash effect, we use a high value like 1.0. In the following set of examples, we adjusted the emission angle to showcase Explosiveness better.

0.3 Explosiveness 0.5 Explosiveness 1.0 Explosiveness

We use the Texture property to tell the particles’ look. For example, to make a snow rain, we can use a single snowflake as Texture.

Sometimes a particle effect should emit only once, as seen in splashes or explosions. In those cases, we enable the One Shot property. This property disables Emitting as soon as any particle is at the end of its Lifetime.

One Shot false One Shot true

When we want each particles’ transform to be global, we toggle off the Local Coord property. This is necessary for many effects like trails, after images, smoke, fire, explosions, weather, and more.

Local Coords true Local Coords false

About Process Material

The Process Material is a shader Godot uses to draw and animate the particles. It’s the most important Particles2D property. Without it, the node won’t even emit the particles.

We have two options to control particles:

  1. Godot provides a ParticlesMaterial resource with many properties like Velocity, Color, Scale, and more. We use it throughout this guide.
  2. When the ParticlesMaterial isn’t enough, we can write a custom shader using Godot’s shader language.

These features use the particles’ lifetime as a base for their effects. For instance, we can create a gradient like this:

This means each particle starts blue, and as time goes it becomes pink.

The same goes for other properties. As another example, take a look at this curve in the Scale > Scale Curve property:

It means that each particle starts small, grows bigger, and shrinks until it disappears by the end of its lifetime.

How to use Particles2D in practice

Let’s see some concrete ways to use particles in your 2D games.

Drawing trails

You can use particles to draw trails, like smoke coming out of a car’s exhaust pipe or plasma behind a ship.

Trails like that emphasize movement.

For a trail like the above, we use a low Time -> Lifetime and a high Amount, causing many particles to spawn close to each other and fade out quickly.

We use a circle with blurred edges for the Textures -> Texture so our trail’s many particles blend together.

The Lifetime and Amount respectively dictate the trail’s length and quality. The more particles you add, the fewer artifacts you get.

In this demo, we set the Amount to 80 and the Time -> Lifetime to 0.5.

We also turned off Drawing -> Local Coords so particles move independently of parent nodes. Otherwise, they’ll turn and move with the ship.

To make the trail taper off, we assign a ParticlesMaterial to our Process Material -> Material and assign a CurveTexture to the Scale -> Scale Curve.

By making the curve go down, our particles scale down over their lifetime.

Also, we zero out the material’s Gravity -> Gravity as, by default, particles fall.

To give our spaceship two trails, we duplicate the Particles2D node.

The Particles2D node has an Emitting property that tells if it should or shouldn’t draw particles.

For this effect, we only want to draw particles when the spaceship is moving.

In the spaceship’s script, we toggle the left and right thrust trails when the player presses or releases the move_up action.

onready var _trail_left := $TrailParticles2DLeft
onready var _trail_right := $TrailParticles2DRight

func _unhandled_input(event: InputEvent) -> void:
    _trail_left.emitting = Input.is_action_pressed("move_up")
    _trail_right.emitting = Input.is_action_pressed("move_up")

Dust puffs

Another use case for particles is to create dust puffs when the player characters land on the floor.

For this effect to work, we need a texture resembling clouds in the Texture property.

In this demo, we set the Lifetime to 0.7 as dust dissipates fast.

As the dust puffs appear as soon as the feet touch the ground, we use a high Explosiveness. In this demo, we set it to 0.9.

The particles should stay where the character’s feet touched the ground. So we also disable the Drawing -> Local Coords.

Then, for the particles’ behavior, we use a ParticlesMaterial in the Process Material -> Material property.

In the ParticlesMaterial, we disable gravity. So every axis of the Gravity property has a value of zero. This prevents them from the sense of weight.

We also set the Direction X-axis to 1.0 and the Y and Z axes to 0.0. This makes the particles emit towards the right. And we use a Spread of 24 to create a cone of emission.

The Spread allows the particles to move in different directions. But for it to work, we need some Initial Velocity. So we set this property to 60.0.

To make the dust clouds puff, we play with the Scale -> Scale Curve. We use three points in the curve to draw this shape.

The graph means the particle starts with a small scale. Then it grows fast to the final scale. Then it shrinks until it disappears.

Note that we set the Scale -> Scale property to 0.6. This property controls the maximum scale of each particle.

We use a gradient in the Color -> Color Ramp property to tint the texture and fade it at the end of its lifetime.

To use the effect in-game, we create a scene with two instances of the particles. Each of them spawns dust on both sides of the character.

In the left one, we have the ParticlesMaterial unique to the LeftPuffParticles2D. This is because we want to make the particles move towards the left. To do so, you have to change the Direction’s X-axis value to -1.0.

Although our dust is a one-time effect, we didn’t use the One Shot property here. It causes some issues with quick effects like the one we have.

Instead, we use an AnimationPlayer to control the emission. Our animation also calls the queue_free() method on the root node. With this approach, we remove it from the scene as soon as the effect finishes.

With the animation set to autostart, it should play as soon as the effect appears on the scene.

All we have to do then is instantiate the DustPuffParticles scene when a character lands. For that, we used a Spawner. It’s a node responsible for adding nodes to the game world.

extends Position2D

export (PackedScene) var spawnling_scene


func spawn() -> Node2D:
    var spawnling: Node2D = spawnling_scene.instance()
    spawnling.set_as_toplevel(true)
    spawnling.global_position = global_position
    add_child(spawnling)
    return spawnling

Then, we add a Spawner to the character’s scene and set its Spawnling Scene property to point to the DustPuffParticles2D scene file.

To trigger the effect we used the BasePlayerSideScroll is_landing() method in the DustPuffPlayerSideScroll.

## Returns `true` if the character is landing on the floor this frame.
func is_landing() -> bool:
    # When the _snap_vector is zero, it means the character jumped
    # If the character jumped then hit the floor it means it landed
    return _snap_vector == Vector2.ZERO and is_on_floor()
    if is_landing():
        _dust_puff.spawn()

Water splash

Splashing some water when the character enters a pool, river, or lake is a good addition to any game’s feel.

For this effect, we use a texture that resembles a droplet.

Since the droplets spread in all directions we use a high Amount to maintain the effect’s shape. In the demo, we use a value of 64.

For the Lifetime we set a value of 1.5. This gives them enough time to animate properly and disappear with a natural feel.

Here we use a value of 0.1 for the Preprocess property. This property skips the particles drawing to a point ahead in time. We use this to make the droplets appear in positions a bit away from the emission point.

Almost all droplets must appear as soon as the character enters the water. So we set the Explosiveness to 0.95.

Again, we toggle off the Local Coords property. This prevents the particles from moving with the Particles2D node.

We want the particles to fall fast to a sense of weight to them. For that, in the ParticlesMaterial we use a value of 400.0 in the Gravity y-axis.

The particles should move upwards to give the idea that they come from the water as the player entered it. For that, we set the Direction Y axis to -1.0 and 0.0 in X and Z axes.

The particles only move if they have an Initial Velocity. So we set it 400.0. To add variation to the movement, we’ve set the Velocity Random to 0.6.

This finishes the basic movement that each droplet should make.

Notice that it looks a bit weird as the texture doesn’t follow the movement. To fix that, we toggle on the Flags -> Align Y property. This setting uses the particle’s movement vector to update its rotation.

We can add randomness to each particle’s lifetime. This way they disappear at different duration, which is more natural. For that, we set the Time -> Lifetime Randomness to 0.5.

We also create some streams of water so that some particles move one after the other in the same direction. The Trail -> Divisor property takes the Amount and creates some particles trails. In the demo, we use a value of 4. With that, it creates a total of 16 trails.

With the Initial Velocity we set, the particles go too far from the emission point. This breaks the effect’s shape. To fix that, we want to decelerate them. This narrows the effect a bit. To decelerate them we can use the Damping property. In the demo, we’ve set it to 60.0 and the Damping Random to 0.3.

Finally, to make the droplets look like they are dissipating, we animate their scale.

In the Scale category, we set the Scale to 0.5 to decrease the particles’ maximum size. Then, to add variation to their scale we’ve set the Scale Random to 0.25. Then we use the following curve to animate their size along their lifetime:

As you can see, they start with half their final scale, then shrink and disappear. This gives a sense of depth. They grow a bit as if they were moving towards the camera.

To tint the texture, we create a Gradient in the Color -> Color Ramp property. The gradient goes from a semi-transparent blue to a completely transparent one at the end. With that, the droplets fade as they get closer to their lifetime duration.

To emit this effect we use a similar approach to the Dust Puff. We use an AnimationPlayer with an autoplay animation to control the Emit property. It also calls the queue_free() method on the root node.

And like the Dust Puff, to use this effect in practice we rely on a Spawner in the player’s character scene.

To trigger the spawn of the effect we use an Area2D to detect when the character touches a water area.

func _ready() -> void:
    _water_detecting_area.connect("area_entered", self, "_on_WaterDetectingArea2D_area_entered")

Then we use a threshold to know if the speed the character fell on the water was enough to create a splash.

func _on_WaterDetectingArea2D_area_entered(area: Area2D) -> void:
    if _velocity.y < splash_fall_threshold:
        return
    _water_splash_spawner.spawn()

Blood spills

It’s interesting to provide visual feedback when something hurts the player’s character. Spilling blood at the point of injury is a good way to do that.

For the blood spills, we use an approach like the water splash shown before. The properties’ values change, but the concept is the same. For reference, these are the settings for the BloodParticles2D node:

As for the ParticlesMaterial:

Unlike the water splash, the BloodParticles2D goes as a direct child to the player character.

The way we trigger its emission is through a method that we call on the player when something hurts them.

func get_hurt(motion: Physics2DTestMotionResult) -> void:
    _blood_particle.rotation = motion.collision_normal.angle()
    _blood_particle.global_position = motion.collision_point
    _blood_particle.emitting = true
    _anim_player.play("hurt")

Note that the method asks for a motion parameter. This is a Physics2DTestMotionResult. It has all the collision data we need to rotate and position the effect. We do that to make the effect fit the injury position and the movement direction.

So, to provide this data, we use objects that can hurt other objects. Upon collision, we create a Physics2DTestMotionResult testing the movement towards the collision body. Then we check if they collided with something hurtable. For that, we check if the object has the get_hurt() method. If this is the case, we call the method on the object.

func _on_body_entered(body: Node) -> void:
    var motion_test_result := Physics2DTestMotionResult.new()

    test_motion(body.global_position, true, 0.08, motion_test_result)
    _pivot.rotation = motion_test_result.collider_velocity.angle()

    if body.has_method("get_hurt"):
        body.get_hurt(motion_test_result)

With that, no matter where the character got hurt, blood spills there!

Simulating weather

Using the right textures, we can simulate many weather and environmental effects such as rain, snow, or leaves falling on an autumn day. We can even make sakura petals fall: an interesting effect for Japanese visual novels.

Spring Summer Autumn Winter

Each of these effects uses different Texture, Amount, Lifetime, and ParticlesMaterial settings. So we are going to focus on what they have in common: Emission Shape.

In the ParticlesMaterial we can play with different Emission Shapes. Until now we created our particles from a single point. But we can do it differently.

There are five types of Emission Shapes, here we focus on the three main ones: Point, Sphere, and Box. We talk about Points and Directed Points in the Emission Masks section.

Point Sphere Square

In our demo, we use the Square shape with X equal to the screen’s width, in this case, 1920.0. With that, the particles can spawn anywhere across the screen. So we move the Particles2D to the top portion of the scene. Then, we increase the ParticlesMaterial -> Gravity.

This makes it look like they are falling from the sky. This approach is good for side view games.

There’s also a bonus environment effect we use in some of our space-themed scenes: the starfield. It uses the same concept, but with X and Y axes corresponding to the screen width and height. In this one, we zero out the ParticlesMaterial -> Gravity, as these stars aren’t meant to fall.

Charging effect

Many games have attacks that players can charge before firing. For that, they gather energy from the environment. For example, in Mega Man X series, we can charge the X Buster for a powerful shot.

Let’s see how to make this effect using Particles2D and an AnimationPlayer.

The Charge effect consists of two core aspects:

For the effect’s Texture, we use an image of two overlapping circles. A solid one and a blurry one, which simulates glowing.

The Particles2D setup consists of a total Amount of 16 particles. We also set the Local Coords to false and that’s it. The real trick comes in the ParticlesMaterial properties.

To create the energy area around the character we change the Emission Shape -> Shape to a Sphere. It should have 60.0 pixels of radio.

For that movement where the particles go towards the center, we did the following:

  1. Disabled the Gravity, setting all axes to 0.0.
  2. Set the Radial Accel -> Accel to -80.0.
  3. Set the Radial Accel -> Accel Random to 0.3.

Radial acceleration moves the particles based on the Particles2D position. So a negative value moves them towards it, while a positive moves them away from the emission’s center.

100 Radial Accel -100 Radial Accel

Finally, we created this curve for the Scale -> Scale Curve. It makes it so that the particles look like appearing from thin air as the caster charges the energy. We also decrease the Scale -> Scale to 0.5. And for a variation on their size, we set the Scale -> Scale Random to 0.3.

Now, to create the feeling of anticipation, we use an animation. Here we play with the Particles2D -> Speed Scale. The Speed Scale multiplies the particles’ processing speed.

We animate it from 0.5 to 2.0 within a second. With that, we can play with slowed and accelerated versions of the effect.

If your computer supports GLES3, you can also play with the Self Modulate property. Here we animate it from default white to 1.5 white. With that, the particles glow dynamically in a scene with a World Environment that has Glow turned on.

We can play this animation to use the effect in the final scene.

The demo has another animation that changes the properties in other nodes. As an example, it triggers the laser beam and ProgressBar for the charge-up UI. We also play the Particles2D inside this animation as well.

By relying on the AnimationPlayer like that, the actual code becomes quite small:

func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed("shoot"):
        _anim_player.play("charge")
        
    # Resets the character's pose
    if event.is_action_released("shoot") and can_cancel:
        _anim_player.play("RESET")

Rocket thrust

We can also use Particles2D to simulate effects that would be too complex to draw by hand. One of these things is fire and smoke.

Using textures and a gradient, we can simulate the look of a rocket thrust using particles.

Using the ParticlesMaterial -> Color Ramp property, we can tint the texture’s color over time. That’s the most important feature we use in this example.

Here are the base Particles2D properties we use for this effect:

Next is the ParticlesMaterial.

There, we set the Direction -> Direction to point downwards. So the values for each axis are X of 0.0, Y of 1.0, and Z of 0.0. We do that to match the spaceship’s exhaust fan position in the Sprite.

As in many other effects, we zero out the Gravity to prevent the particles from falling.

To create cone-like emission, we set the Direction -> Spread to 15.0 degrees.

We need a value above zero in the Initial Velocity -> Velocity for the material to take the emission direction into account. So we set this property to the arbitrary value of 1.0.

To make the whole trail look attractive, we animate the particles’ angle.

To do so, we add some Angular Velocity -> Velocity and randomize the value.

To randomize the start angle of each particle, we change the Angle -> Angle and Angle -> Angle Random properties. This adds visual variation to our particles and makes the trail look appealing.

To make it look like the smoke dissipates, we play with the Scale -> Scale and Scale -> Scale Curve. Smoke spreads in the air, so we increase the scale along the particle’s lifetime to get that feel.

To make the trail look like fire, we assign a color gradient to the Color -> Color Ramp property.

This property tints the particle along its lifetime.

So to make it look like fire, we start with white, then yellow, orange, red, dark gray, then the same dark gray but transparent.

This last transparent color makes the particles fade out instead of disappearing instantly.

Combining those settings leads to an appealing engine trail.

Note that to recolor the particles, you need a white texture like our transparent puff.

The color options tint the texture by multiplying the colors.

If your texture uses any tone other than pure white, the color ramp will darken the texture.

This behavior is consistent with the Modulate property of 2D nodes in Godot.

Explosion

Explosions are one of the main visual effects game developers can have on their sleeves. There are two main approaches to creating explosions:

Flipbook Result

There’s also a middle ground. You can play with some pre-rendered images in a procedural explosion. You get the best of both worlds and diminish the downsides.

In our demo, we used stylized graphics. So we can go straight with the engine simulation approach using simplified textures.

This effect shows how to layer Particles2D nodes to create complex and rich results. Four layers compose the explosion:

Smoke Fire trails Fire burst Sparkles

The smoke layer uses a long Lifetime to dissipate the explosion smoke slowly.

The fire trail is a composition of five Particles2D. We use the ParticlesMaterial -> Trail properties to simulate flying debris.

As for the fire burst, we quickly grow then shrink many particles. The trick is the short Lifetime, high Amount, and the Scale Curve.

Then, for the sparks, we play with Scale Curve as well, but also with Radial Accel and Tangential Accel.

This effect is part of our 2D VFX Secrets course. There we go in-depth in it. So if you want to know more in detail, you can follow up there.

To coordinate all these layers together and trigger them in sequence, we use an animation.

This animation automatically plays as soon as the effect enters the scene.

The animation toggles the emission of each Particles2D. Note that at the end, we call the queue_free() method on the Explosion node.

This explosion plays once then disappears. We instantiate a new copy every time we need to play the explosion animation.

Twirl effect

To enrich the scenarios of our games we can add all sorts of visual effects. For instance, in space games, it’s cool to have a black hole here and there. We can even turn the effect into a mechanic that pulls objects towards it.

We can make the visual part of these effects with some ParticlesMaterial properties. The main ones are:

We’ve already seen how Radial Acceleration works. So let’s see Orbit Velocity in action now:

Orbit Velocity 0.2 Orbit Velocity 1.0

You can already see how these properties can work together to create a whirl, right? One pulls particles towards the center, the other spins particles around the center.

In the Blackhole demo, we’ve set up the Blackhole as below:

There’s an animation that autoplays. It makes the blackhole spin.

Then, in the script, we use the Tween to animate the Scale from Vector2(1.0, 1.0) to Vector2(1.5, 1.5) and back. We use TRANS_ELASTIC and TRANS_BACK for better interpolation.

The basic setup for the Particles2D is the following:

Then for the ParticlesMaterial:

We use this curve to speed up the spin as the stars move.

As for the Orbit Velocity we use a trick. We want to add depth to the movement. So we use the following settings:

With this curve, the stars orbit slower as they get closer to the blackhole’s center. This approach gives the sensation of distance.

We also use these values in the Scale properties:

We designed the curve above to give the idea that the stars appear from deep space. Then they move closer to the camera. Finally, the blackhole sucks them in, making them shrink to distance.

And this is a brief glance at the final look.

Note that we use a special WorldEnvironment with Glow. So we’ve bumped up the Particles2D -> Modulate to the following values:

Aura effect

Imagine that your characters entered in “awakening” mode. You may want some energy to flow around them, right?

For this type of effect, the default shapes in the ParticlesMaterial -> Emission Shape aren’t enough. Instead, we can use an Emission Mask.

There are two kinds of Emission Masks. In the below example we have the image we’ve used for the mask. Then the three types of Emission Masks.

Mask Solid Pixels Border Pixels Directed Border Pixels

Note that the particles don’t appear at the emission center. This is due to how Godot maps the image points.

For more details on how these three modes work, you can read the Godot official documentation on Emission Masks.

In our demo, we use the Border Pixels option to map the particles around the spaceship.

The settings for the AuraParticles2D follow:

As for the ParticlesMaterial, we work in the Scale category. These changes make the particles look like sparks.

The final touch is using this effect in a WorldEnvironment with Glow active. Then, we bumped up the AuraParticles2D Modulate. Remember, to trigger the glow the color must be above the WorldEnvironment -> Glow -> HDR Threshold.

The design behind this effect is that once players enter “turbo mode,” they emit this aura as if the spaceship is surrounded by pure energy. So, these particles aren’t always emitting. Instead, they only emit when the player presses the turbo key.

You can, of course, add some extra limitations. As an example, you can add some special fuel or energy that depletes in turbo mode. But in our case is just a matter of pressing a button:

    # Turbo effect
    if Input.is_action_just_pressed("turbo"):
        speed = turbo_speed
        _aura.emitting = true
    elif Input.is_action_just_released("turbo"):
        speed = max_speed
        _aura.emitting = false

Your questions

My target devices only support GLES2. How do I simulate particles?

If your game uses the GLES2 renderer, you won’t have access to the Particles2D node. Instead, you’ll need to use CPUParticles2D.

You can follow the guides using the CPUParticles2D node instead of Particles2D. The ParticlesMaterial properties will be in the node itself instead of part of a material resource.

With GLES2, you can’t use graphics acceleration to move the particles. The CPUParticles2D uses the processor (CPU) instead of the graphics card to move particles. This is a constraint imposed by the older GLES2 technology.

When using CPUParticles2D, you’ll need to be careful with the number of particles you spawn, as they’ll compete with the rest of your code.

The default ParticlesMaterial isn’t enough. Can I modify it?

Yes. You can turn it into a ShaderMaterial and tweak it as you please using the shader editor.

For that, right-click the Material -> ParticlesMaterial resource and select Convert to ShaderMaterial.

How can I animate a flipbook texture for my particles effects?

We use this approach when we have a texture with a sequence of images that animate along a particle lifetime.

To do that, we need to create a CanvasItemMaterial in the Material -> Material property. Then we toggle on the Particles Animation property. After that, we set up the flipbook frames according to the texture.

Then in the ParticlesMaterial, there’s a category of properties to control how the animation should play along the particle’s lifetime.

Where can I go to learn more about particle effects?

We recommend looking at the official docs for Particles2D and shaders:

Then, to go further, we have the 2D VFX Secrets course.

We use particles extensively there and go deep into how each effect works.